home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
DDJ9205.ARJ
/
CRCMAN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-08
|
12KB
|
405 lines
/************************** Start of CRCMAN.C *************************
*
* This program is used to build a list of CRC-32 values for all of the
* files in a given directory tree. After building the file, the program
* can be run later to verify the CRC values, giving assurance of the
* integrity of the files. To build the CRC file, the command line is:
*
* CRCMAN -b root-dir crc-file-name
*
* To check the list of files created, run with this command line:
*
* CRCMAN crc-file-name
*
* This program should work with most 16 and 32 bit compilers under
* MS-DOS and UNIX.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
unsigned long CRCTable[ 256 ];
/*
* To build this program under UNIX, define UNIX either on the command
* line or by editing this file. To define it on the command line, the
* program should be built like this:
*
* cc -o crcman -DUNIX crcman.c
*
* The code in this program assumes that the UNIX compiler is of the
* K&R variety, and does away with real function prototyping.
*/
#ifdef UNIX
#include <varargs.h>
#ifdef M_XENIX
#include <sys/ndir.h>
#else
#include <sys/dirent.h>
#endif /* M_XENIX */
#define SEPARATOR "/"
#define FILENAME_SIZE 81
void FatalError();
unsigned long CalculateFileCRC();
void ProcessAllFiles();
void BuildCRCFile();
void CheckFiles();
unsigned long CalculateBufferCRC();
void BuildCRCTable();
#else /* not UNIX, must be MSDOS */
/*
* Most MS-DOS compilers have converged on the same names for the
* structures and functions used when searching directories.
* Unfortunately, Borland C implementations still use a variant,
* which requires a few macro definitions to work around. The
* functions work in an identical manner, so the actual
* implementation of the code is straightforward. The addition of
* the MSDOS definition helps convince the Zortech compiler to use
* the same structure and function names as everyone else.
*
*/
#define MSDOS 1
#include <stdarg.h>
#include <dos.h>
#define SEPARATOR "\\"
#define FILENAME_SIZE FILENAME_MAX
#ifdef __TURBOC__
#include <dir.h>
#define FILE_INFO struct ffblk
#define FIND_FIRST( n, i ) findfirst( ( n ), ( i ), FA_DIREC )
#define FIND_NEXT( info ) findnext( ( info ) )
#define FILE_NAME( info ) ( ( info ).ff_name )
#else
#define FILE_INFO struct find_t
#define FIND_FIRST( n, i ) _dos_findfirst( (n), _A_SUBDIR, (i) )
#define FIND_NEXT( info ) _dos_findnext( ( info ) )
#define FILE_NAME( info ) ( ( info ).name )
#endif
void FatalError( char *fmt, ... );
unsigned long CalculateFileCRC( FILE *file );
void ProcessAllFiles( char *path, FILE *crc_file );
void BuildCRCFile( char *input_dir_name, char *crc_file_name );
void CheckFiles( char *crc_file_name );
unsigned long CalculateBufferCRC( unsigned int count, unsigned long crc,
void *buffer );
void BuildCRCTable( void );
#endif /* UNIX */
/*
* The main program is fairly simple. It checks for valid occurences
* of the two different types of command lines, and executes them if
* found. Otherwise, it prints out a simple usage statement and exits.
*/
int main( argc, argv )
int argc;
char *argv[];
{
setbuf( stdout, NULL );
BuildCRCTable();
if ( argc == 2 )
CheckFiles( argv[ 1 ] );
else if ( argc == 4 && strcmp( argv[ 1 ], "-b" ) == 0 )
BuildCRCFile( argv[ 2 ], argv[ 3 ] );
else {
printf( "Usage: CRCMAN [-b input_dir] crc-file \n" );
printf( "\n" );
printf( "Using the -b option checks all files under the input_dir\n" );
printf( "and appends their data to the crc-file. Otherwise, the\n" );
printf( "program checks the CRC data of all of the files in the\n" );
printf( "crc-file and prints the results\n" );
return( 1 );
}
return( 0 );
}
/*
* Instead of performing a straightforward calculation of the 32 bit
* CRC using a series of logical operations, this program uses the
* faster table lookup method. This routine is called once when the
* program starts up to build the table which will be used later
* when calculating the CRC values.
*/
#define CRC32_POLYNOMIAL 0xEDB88320L
void BuildCRCTable()
{
int i;
int j;
unsigned long crc;
for ( i = 0; i <= 255 ; i++ ) {
crc = i;
for ( j = 8 ; j > 0; j-- ) {
if ( crc & 1 )
crc = ( crc >> 1 ) ^ CRC32_POLYNOMIAL;
else
crc >>= 1;
}
CRCTable[ i ] = crc;
}
}
/*
* The routine to check the CRC values for a list of files has a
* fairly easy job of it. It just reads in a line at a time from
* the CRC file. Each line contains a file name and a CRC value.
* The program then just has to calculate the actual CRC for that
* file, and compare it with the current calculated value. Any
* discrepancy triggers an error message.
*/
void CheckFiles( crc_file_name )
char *crc_file_name;
{
FILE *crc_file;
FILE *test_file;
unsigned long log_crc;
unsigned long crc;
char log_name[ FILENAME_SIZE ];
int result;
crc_file = fopen( crc_file_name, "r" );
if ( crc_file == NULL )
FatalError( "Couldn't open the log file: %s\n", crc_file_name );
for ( ; ; ) {
result = fscanf( crc_file, "%lx %s", &log_crc, log_name );
if ( result < 2 )
break;
test_file = fopen( log_name, "rb" );
if ( test_file != NULL ) {
printf( "Checking %s ", log_name );
crc = CalculateFileCRC( test_file );
fclose( test_file );
if ( crc != log_crc )
printf( "Error: Expected %08lx, got %08lx\n",
log_name, log_crc, crc );
else
printf( "OK\n" );
} else
printf( "Could not open file %s\n", log_name );
}
}
/*
* Building the CRC file is a little harder than just checking
* the file values. This is because the build operation has to
* parse through a directory tree, checking every file. This
* routine defers the hard part of that to a routine called
* ProcessAllFiles(), which takes care of scanning through the
* directory. That means all we have to do here is open the output
* CRC file, and then start the processing. This routine also makes
* sure that the directory name passed on the command line is
* stripped of any trailing '/' or '\' character, since people tend
* to include those when specifying directory names.
*/
void BuildCRCFile( input_dir_name, crc_file_name )
char *input_dir_name;
char *crc_file_name;
{
char path[ FILENAME_SIZE ];
FILE *crc_file;
strcpy( path, input_dir_name );
if ( path[ strlen( path ) - 1 ] == SEPARATOR[ 0 ] )
path[ strlen( path ) - 1 ] = '\0';
crc_file = fopen( crc_file_name, "w" );
if ( crc_file == NULL )
FatalError( "Can't open crc log file: %s\n", crc_file_name );
ProcessAllFiles( path, crc_file );
}
/*
* This routine is responsible for actually performing the
* calculation of the 32 bit CRC for the entire file. We
* precondition the CRC value with all 1's, then invert every bit
* after the entire file has been done. This gives us a CRC value
* that corresponds with the values calculated by PKZIP and ARJ.
* The actual calculation consists of reading in blocks from the
* file, then updating the CRC with the value for that block. The
* CRC work is done by another the CalculateBufferCRC routine.
*/
unsigned long CalculateFileCRC( file )
FILE *file;
{
unsigned long crc;
int count;
unsigned char buffer[ 512 ];
int i;
crc = 0xFFFFFFFFL;
i = 0;
for ( ; ; ) {
count = fread( buffer, 1, 512, file );
if ( ( i++ % 32 ) == 0 )
putc( '.', stdout );
if ( count == 0 )
break;
crc = CalculateBufferCRC( count, crc, buffer );
}
putc( ' ', stdout );
return( crc ^= 0xFFFFFFFFL );
}
/*
* This is the routine that is responsible for calculating all of
* the CRC values for the files in a given directory. The CRC
* values and the file names are written out to the crc_file. This
* routine is somewhat ugly and hard to read because of all the
* conditional code. When searching through directories under
* MS-DOS and UNIX, the flow of control is identical, but all of the
* function calls, structure names, and elements are different.
*
* This routine sits in a loop for each directory, opening each file
* and processing it. Before a file is opened, a check is made to
* see if the file is actually a directory. If it turns out that
* the file is a directory, a new path name is constructed, and this
* routine calls itself recursively so that all the files in the
* subdirectory are also processed.
*/
void ProcessAllFiles( path, crc_file )
char *path;
FILE *crc_file;
{
#ifdef UNIX
DIR *dirp;
#ifdef M_XENIX
struct direct *entry;
#else
struct dirent *entry;
#endif /* M_XENIX */
#define NAME entry->d_name
#else
FILE_INFO fileinfo;
int done;
#define NAME FILE_NAME( fileinfo )
#endif
char fullname[ FILENAME_SIZE ];
struct stat buf;
unsigned long crc;
FILE *file;
printf( "Searching %s\n", path );
strcat( path, SEPARATOR );
#ifdef UNIX
dirp = opendir( path );
if ( dirp == NULL )
FatalError( "Error opening directory %s\n", path );
entry = readdir( dirp );
while ( entry != 0 ) {
#else
strcpy( fullname, path );
strcat( fullname, "*.*" );
done = FIND_FIRST( fullname, &fileinfo );
while ( done == 0 ) {
#endif
strcpy( fullname, path );
if ( strcmp( NAME, "." ) && strcmp( NAME, ".." ) ) {
strcat( fullname, NAME );
if ( stat( fullname, &buf ) == -1 )
FatalError( "Error reading stat from file %s!\n", fullname );
if ( buf.st_mode & S_IFDIR )
ProcessAllFiles( fullname, crc_file );
else {
file = fopen( fullname, "rb" );
if ( file != NULL ) {
printf( "Scanning %s ", fullname );
crc = CalculateFileCRC( file );
putc( '\n', stdout );
fprintf( crc_file, "%08lx %s\n", crc, fullname );
fclose( file );
} else
printf( "Could not open %s!\n", fullname );
}
}
#ifdef UNIX
entry = readdir( dirp );
#else
done = FIND_NEXT( &fileinfo );
#endif
}
}
/*
* This routine calculates the CRC for a block of data using the
* table lookup method. It accepts an original value for the crc,
* and returns the updated value.
*/
unsigned long CalculateBufferCRC( count, crc, buffer )
unsigned int count;
unsigned long crc;
void *buffer;
{
unsigned char *p;
unsigned long temp1;
unsigned long temp2;
p = (unsigned char*) buffer;
while ( count-- != 0 ) {
temp1 = ( crc >> 8 ) & 0x00FFFFFFL;
temp2 = CRCTable[ ( (int) crc ^ *p++ ) & 0xff ];
crc = temp1 ^ temp2;
}
return( crc );
}
/*
* The fatal error handler just has to print out a formatted error
* message and then exit. The difficulty in this routine lies in
* the different way that variable numbers of arguments are
* processed under ANSI C and K&R C. The body of the routine is
* the same under both environments, but the declaration of the
* function and its arguments differs quite a bit.
*/
#ifdef UNIX
void FatalError( va_alist )
va_dcl
{
char *fmt;
va_list argptr;
va_start( argptr );
fmt = va_arg( argptr, char * );
#else
void FatalError( char *fmt, ... )
{
va_list argptr;
va_start( argptr, fmt );
#endif
printf( "Fatal error: " );
vprintf( fmt, argptr );
va_end( argptr );
exit( -1 );
}
/************************** End of CRCMAN.C ****************************/